﻿#region Copyright (c) 2009, Active Web Solutions Ltd.
// Copyright (c) 2009, Active Web Solutions Ltd
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:

// * Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.

// * Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the
//   documentation and/or other materials provided with the distribution.

// * Neither the name of Active Web Solutions Ltd nor the names of its
//   contributors may be used to endorse or promote products derived from
//   this software without specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion

using System;
using System.Diagnostics;
using System.IO;
using System.Net.Sockets;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Threading;
using Microsoft.ServiceBus;

namespace Aws.SocketShifter
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    public class SocketShifterServer : IServer
    {
        private TcpClient tcpClient;
        private NetworkStream stream;
        private IClient client;

        public static ServiceHost CreateServiceHost(string issuerName, string issuerSecret, string servicePath)
        {
            // Create the credentials object for the endpoint
            TransportClientEndpointBehavior sharedSecretServiceBusCredential = new TransportClientEndpointBehavior();
            sharedSecretServiceBusCredential.CredentialType = TransportClientCredentialType.SharedSecret;
            sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerName = issuerName;
            sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerSecret = issuerSecret;

            ServiceHost serviceHost = new ServiceHost(typeof(SocketShifterServer));

            ServiceEndpoint endpoint = serviceHost.AddServiceEndpoint(typeof(IServer), new NetTcpRelayBinding(),
                    servicePath);

            endpoint.Behaviors.Add(sharedSecretServiceBusCredential);

            return serviceHost;
        }

        private void TopLoop()
        {
            while (tcpClient.Connected)
            {
                byte[] readBuffer = new byte[tcpClient.ReceiveBufferSize];

                try
                {
                    int n = stream.Read(readBuffer, 0, readBuffer.Length);
                    if (n > 0)
                    {
                        byte[] writeBuffer = new byte[n];
                        Array.Copy(readBuffer, writeBuffer, n);
                        Debug.Write(">");
                        client.Receive(writeBuffer);
                    }
                }
                catch (Exception e)
                {
                    Debug.WriteLine(e.ToString());
                    break;
                }
            }
        }

        public bool Connect(string host, int port)
        {
            ConnectionRules connectionRules = new ConnectionRules();

            if (connectionRules.Check(host, port))
            {
                try
                {
                    tcpClient = new TcpClient(host, port);
                } 
                catch (SocketException e) 
                {
                    // Cant connect to given host and port
                    Debug.WriteLine(e);
                    return false;
                }

                stream = tcpClient.GetStream();

                client = OperationContext.Current.GetCallbackChannel<IClient>();

                Thread thread = new Thread(new ThreadStart(TopLoop));
                thread.Start();
                return true;
            }
            else
            {
                Debug.WriteLine("Connection disallowed - have you configured this host and port in your allow list?");
                return false;
            }
        }

        public void Send(byte[] buffer)
        {
            Debug.Write("<");
            try
            {
                stream.Write(buffer, 0, buffer.Length);
            }
            catch (IOException e)
            {
                Debug.WriteLine(e);
            }
            catch (ObjectDisposedException e)
            {
                Debug.WriteLine(e);
            }
        }

        public void Disconnect()
        {
            stream.Close();
            tcpClient.Close();
            Debug.WriteLine("\nDisconnect");
        }
    }
}
